package cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.XiaoBizHandlerCodegenDetailRespVO;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.XiaoBizHandlerCodegenPreviewRespVO;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.XiaoBizHandlerCodegenUpdateReqVO;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.table.XiaoBizHandlerCodegenTablePageReqVO;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.table.XiaoBizHandlerCodegenTableRespVO;
import cn.iocoder.yudao.module.xiaoBizHandler.controller.admin.codegen.vo.table.XiaoBizHandlerSchemaTableRespVO;
import cn.iocoder.yudao.module.xiaoBizHandler.convert.codegen.XiaoBizHandlerCodegenConvert;
import cn.iocoder.yudao.module.xiaoBizHandler.dal.dataobject.codegen.XiaoBizHandlerCodegenColumnDO;
import cn.iocoder.yudao.module.xiaoBizHandler.dal.dataobject.codegen.XiaoBizHandlerCodegenTableDO;
import cn.iocoder.yudao.module.xiaoBizHandler.dal.dataobject.codegen.XiaoBizHandlerSchemaTableDO;
import cn.iocoder.yudao.module.xiaoBizHandler.dal.dataobject.config.XiaoBizHandlerConfigDO;
import cn.iocoder.yudao.module.xiaoBizHandler.service.codegen.XiaoBizHandlerCodegenService;
import cn.iocoder.yudao.module.xiaoBizHandler.service.config.XiaoBizHandlerConfigService;
import com.alibaba.fastjson.JSONObject;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

@Api(tags = "管理后台 - 代码生成器")
@RestController
@RequestMapping("/xiaoBizHandler/codegen")
@Validated
@Slf4j
public class XiaoBizHandlerCodegenController {

    @Resource
    private XiaoBizHandlerCodegenService xiaoBizHandlerCodegenService;

    @Resource
    private XiaoBizHandlerConfigService xiaoBizHandlerConfigService;

    @Value("${isDevelopmentEnv}")
    private String isDevelopmentEnv;

    @GetMapping("/db/table/list")
    @ApiOperation(value = "获得数据库自带的表定义列表", notes = "会过滤掉已经导入 Codegen 的表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tableName", value = "表名，模糊匹配", required = true, example = "yudao", dataTypeClass = String.class),
            @ApiImplicitParam(name = "tableComment", value = "描述，模糊匹配", required = true, example = "芋道", dataTypeClass = String.class)
    })
    @PreAuthorize("@ss.hasPermission('xbh:codegen:query')")
    public CommonResult<List<XiaoBizHandlerSchemaTableRespVO>> getSchemaTableList(
            @RequestParam(value = "tableName", required = false) String tableName,
            @RequestParam(value = "tableComment", required = false) String tableComment) {
        // 获得数据库自带的表定义列表
        List<XiaoBizHandlerSchemaTableDO> schemaTables = xiaoBizHandlerCodegenService.getSchemaTableList(tableName, tableComment);
        // 移除在 Codegen 中，已经存在的
        Set<String> existsTables = CollectionUtils.convertSet(xiaoBizHandlerCodegenService.getCodeGenTableList(), XiaoBizHandlerCodegenTableDO::getTableName);
        schemaTables.removeIf(table -> existsTables.contains(table.getTableName()));
        return success(XiaoBizHandlerCodegenConvert.INSTANCE.convertList04(schemaTables));
    }

    @GetMapping("/table/page")
    @ApiOperation("获得表定义分页")
    @PreAuthorize("@ss.hasPermission('xbh:codegen:query')")
    public CommonResult<PageResult<XiaoBizHandlerCodegenTableRespVO>> getCodeGenTablePage(@Valid XiaoBizHandlerCodegenTablePageReqVO pageReqVO) {
        PageResult<XiaoBizHandlerCodegenTableDO> pageResult = xiaoBizHandlerCodegenService.getCodegenTablePage(pageReqVO);
        return success(XiaoBizHandlerCodegenConvert.INSTANCE.convertPage(pageResult));
    }

    @GetMapping("/detail")
    @ApiOperation("获得表和字段的明细")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:query')")
    public CommonResult<XiaoBizHandlerCodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
        XiaoBizHandlerCodegenTableDO table = xiaoBizHandlerCodegenService.getCodegenTablePage(tableId);
        List<XiaoBizHandlerCodegenColumnDO> columns = xiaoBizHandlerCodegenService.getCodegenColumnListByTableId(tableId);
        // 拼装返回
        return success(XiaoBizHandlerCodegenConvert.INSTANCE.convert(table, columns));
    }

    @ApiOperation("基于数据库的表结构，创建代码生成器的表和字段定义")
    @ApiImplicitParam(name = "tableNames", value = "表名数组", required = true, example = "sys_user", dataTypeClass = List.class)
    @PostMapping("/create-list-from-db")
    @PreAuthorize("@ss.hasPermission('xbh:codegen:create')")
    public CommonResult<List<Long>> createCodegenListFromDB(@RequestParam("tableNames") List<String> tableNames) {
        return success(xiaoBizHandlerCodegenService.createCodegenListFromDB(getLoginUserId(), tableNames));
    }

    @ApiOperation("基于 SQL 建表语句，创建代码生成器的表和字段定义")
    @ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
    @PostMapping("/create-list-from-sql")
    @PreAuthorize("@ss.hasPermission('xbh:codegen:create')")
    public CommonResult<Long> createCodegenListFromSQL(@RequestParam("sql") String sql) {
        return success(xiaoBizHandlerCodegenService.createCodegenListFromSQL(getLoginUserId(), sql));
    }

    @ApiOperation("更新数据库的表和字段定义")
    @PutMapping("/update")
    @PreAuthorize("@ss.hasPermission('xbh:codegen:update')")
    public CommonResult<Boolean> updateCodegen(@Valid @RequestBody XiaoBizHandlerCodegenUpdateReqVO updateReqVO) {
        xiaoBizHandlerCodegenService.updateCodegen(updateReqVO);
        return success(true);
    }

    @ApiOperation("基于数据库的表结构，同步数据库的表和字段定义")
    @PutMapping("/sync-from-db")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:update')")
    public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
        xiaoBizHandlerCodegenService.syncCodegenFromDB(tableId);
        return success(true);
    }

    @ApiOperation("基于 SQL 建表语句，同步数据库的表和字段定义")
    @PutMapping("/sync-from-sql")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class),
            @ApiImplicitParam(name = "sql", value = "SQL 建表语句", required = true, example = "sql", dataTypeClass = String.class)
    })
    @PreAuthorize("@ss.hasPermission('xbh:codegen:update')")
    public CommonResult<Boolean> syncCodegenFromSQL(@RequestParam("tableId") Long tableId,
                                                    @RequestParam("sql") String sql) {
        xiaoBizHandlerCodegenService.syncCodegenFromSQL(tableId, sql);
        return success(true);
    }

    @ApiOperation("删除数据库的表和字段定义")
    @DeleteMapping("/delete")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:delete')")
    public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
        xiaoBizHandlerCodegenService.deleteCodegen(tableId);
        return success(true);
    }

    @ApiOperation("预览生成代码")
    @GetMapping("/preview")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:preview')")
    public CommonResult<List<XiaoBizHandlerCodegenPreviewRespVO>> previewCodegen(@RequestParam("tableId") Long tableId) {
        Map<String, String> codes = xiaoBizHandlerCodegenService.generationCodes(tableId);
        return success(XiaoBizHandlerCodegenConvert.INSTANCE.convert(codes));
    }

    @ApiOperation("下载生成代码")
    @GetMapping("/download")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:download')")
    public void downloadCodegen(@RequestParam("tableId") Long tableId,
                                HttpServletResponse response) throws IOException {
        // 生成代码
        Map<String, String> codes = xiaoBizHandlerCodegenService.generationCodes(tableId);
        // 构建 zip 包
        String[] paths = codes.keySet().toArray(new String[0]);
        ByteArrayInputStream[] ins = codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipUtil.zip(outputStream, paths, ins);
        // 输出
        ServletUtils.writeAttachment(response, "codegen.zip", outputStream.toByteArray());
    }


    @ApiOperation("复制代码到项目中")
    @GetMapping("/copy2Project")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:copy2Project')")
    public CommonResult<JSONObject> copy2Project(@RequestParam("tableId") Long tableId,
                                                 HttpServletResponse response) throws IOException {
        JSONObject rs = new JSONObject();
        rs.put("isDevelopmentEnv",isDevelopmentEnv);
        // 获取是否是开发环境
        if (!"true".equals(isDevelopmentEnv.toLowerCase())) {
            return success(rs);
        }
        // 获取项目的绝对路径
        String configPathKey = "srcRootPath";
        XiaoBizHandlerConfigDO config = xiaoBizHandlerConfigService.getConfigByKey(configPathKey);
        String path = config.getValue();

        String isHaveFileAlready = "false";
        // 校验项目中指定路径是否已经有文件
        // 生成代码
        Map<String, String> codes = xiaoBizHandlerCodegenService.generationCodes(tableId);
        // 遍历所有路径校验是否已经存在文件
        for (String s : codes.keySet()) {
            if (!path.endsWith("\\") && !path.endsWith("/")) {
                path = path + "\\";
            }
            String fileAbsolutePath = path + s;
            if (new File(fileAbsolutePath).exists()) {
                isHaveFileAlready = "true";
                log.info("文件： " + fileAbsolutePath + " 已经存在！");
                break;
            }
        }
        rs.put("isHaveFileAlready", isHaveFileAlready);
        if ("true".equals(isHaveFileAlready.toLowerCase())) {
            return success(rs);
        }
        // 遍历所有路径复制文件
        int index = 0;
        for (Map.Entry<String, String> entry : codes.entrySet()) {
            String relativePath = entry.getKey();
            String fileContent = entry.getValue();

            if (!path.endsWith("\\") && !path.endsWith("/")) {
                path = path + "\\";
            }
            String fileAbsolutePath = path + relativePath;
            File file = new File(fileAbsolutePath);
            int fileCount = Integer.parseInt(xiaoBizHandlerConfigService.getConfigByKey("file_count").getValue());
            if ((index + 1) <= fileCount) {
                if ((index + 1) == 13) {
                    ++index;
                    continue;
                }
                FileUtil.touch(file);
                FileUtil.writeUtf8String(fileContent, file);
                log.info("第 " + (++index)  +" 个文件： " + file.getAbsolutePath() + " 已写入！");
            }
        }


        return success(rs);
    }

    @ApiOperation("撤回-复制代码到项目中")
    @GetMapping("/recallCopy2Project")
    @ApiImplicitParam(name = "tableId", value = "表编号", required = true, example = "1024", dataTypeClass = Long.class)
    @PreAuthorize("@ss.hasPermission('xbh:codegen:recallCopy2Project')")
    public CommonResult<JSONObject> recallCopy2Project(@RequestParam("tableId") Long tableId,
                                                 HttpServletResponse response) throws IOException {
        JSONObject rs = new JSONObject();

        // 获取是否是开发环境
        if (!"true".equals(isDevelopmentEnv.toLowerCase())) {
            rs.put("isDevelopmentEnv","false");
            return success(rs);
        } else {
            rs.put("isDevelopmentEnv", "true");
        }

        // 获取项目的绝对路径
        String configPathKey = "srcRootPath";
        XiaoBizHandlerConfigDO config = xiaoBizHandlerConfigService.getConfigByKey(configPathKey);

        // 调试
        // ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, null, new File("C:\\Users\\xiaosiyuan\\Desktop\\tmp"));
        String path = config.getValue();
        // 生成代码
        Map<String, String> codes = xiaoBizHandlerCodegenService.generationCodes(tableId);

//        ZipUtil.zip(outputStream, StandardCharsets.UTF_8, false, null, new File(path));
        // 校验项目中指定路径是否已经有文件

    // 遍历所有路径复制文件
        int index = 0;
        for (Map.Entry<String, String> entry : codes.entrySet()) {
            String relativePath = entry.getKey();
            String fileContent = entry.getValue();

            if (!path.endsWith("\\") && !path.endsWith("/")) {
                path = path + "\\";
            }
            String fileAbsolutePath = path + relativePath;
            if (FileUtil.del(fileAbsolutePath)) {
                log.info("第 " + (++index)  +" 个文件： " + fileAbsolutePath + " 已删除！");
            }
        }
        return success(rs);
    }

}
